/*
 * price_simulator.cpp
 *
 *  Created on: Apr 2, 2009
 *      Author: Lev
 */

#include <iostream>
#include <algorithm>
#include "price_simulator.hpp"
using namespace std;

SPExperiment& SPExperiment::setSPModelProps( const SPModelProps& sProps ) {
	spModelProps.s0 = sProps.s0;
	spModelProps.mu = sProps.mu;
	spModelProps.sigma = sProps.sigma;
	return *this;
}

void StockPriceSimulator::produceAveragedSequence( const SPExperiment& experiment ) {
	// clear results
	_spResults.clear();
	// run experiments
	for( unsigned nRun = 0; nRun < experiment.nRuns; nRun++ ) {
		// reset _spModel
		_spModel.resetProps( experiment.spModelProps );
		if( nRun == 0 ) {
			// set initial price points
			_spResults.push_back( experiment.spModelProps.s0 );
			for( unsigned i = 1; i < experiment.nPoints; i++ ) {
				double sPrice = _spModel.getNextPrice( experiment.dt );
				// store initial stock price point for this interval
				_spResults.push_back( sPrice );
			}
		} else {
			// put averaged price points to the vector
			for( unsigned i = 1; i < experiment.nPoints; i++ ) {
				double sPrice = _spModel.getNextPrice( experiment.dt );
				double avPrice = _spResults[i];
				_spResults[i] = ((double)nRun*avPrice + sPrice)/(double)(nRun + 1);
			}
		}
	}
}

void StockPriceSimulator::produceOrderedEndpoints( const SPExperiment& experiment ) {
//	cout << "produceOrderedEndpoints()" << endl;
	// clear results
	_spResults.clear();
	// set initial price
	double sPrice = experiment.spModelProps.s0;
	// run experiments
	for( unsigned nRun = 0; nRun < experiment.nRuns; nRun++ ) {
		// reset _spModel
		_spModel.resetProps( experiment.spModelProps );
//		if( nRun == 0 )
//			cout << "SPModel: " << _spModel.getStrikePrice() << "; "
//					<< _spModel.getExpectedReturn() << "; "
//					<< _spModel.getVolatility() << endl;
		// run _spModel nPoints times
		for( unsigned i = 0; i < experiment.nPoints; i++ ) {
			sPrice = _spModel.getNextPrice( experiment.dt );
//			if( nRun == 0 )
//				cout << "SP[" << i << "]=" << sPrice << endl;
		}
		// store end stock price result
		_spResults.push_back( sPrice );
	}
	sort( _spResults.begin(), _spResults.end() );
}

// runs simulation experiment
void StockPriceSimulator::runExperiment( const SPExperiment& experiment )
		throw(invalid_argument) {
	// TODO: check arguments
	_finished = false;
	switch( experiment.rType ) {
		case ORDERED_ENDPOINTS:
			produceOrderedEndpoints( experiment );
			break;
		case AVERAGED_SEQUENCE:
			produceAveragedSequence( experiment );
			break;
		default:
			throw invalid_argument( "invalid result type" );
	}
	_finished = true;
}

SampleEstimations::SampleEstimations( const std::vector<double> &spResults )
		: _spResults( spResults ) {
	// calculate sample mean
	double sum = 0;
	for( SPIter spIter = _spResults.begin(); spIter != _spResults.end(); ++spIter )
		sum += *spIter;
	_mean = _spResults.size() > 0 ? sum/(double)_spResults.size() : 0.0;
	// calculate sample variance
	sum = 0;
	for( SPIter spIter = _spResults.begin(); spIter != _spResults.end(); ++spIter ) {
		double tmp = *spIter - _mean;
		sum += tmp*tmp;
	}
	_variance = _spResults.size() > 1 ? sum/(double)(_spResults.size() - 1) : sum;
}

// returns the value, below which a certain percent of results fall
double SampleEstimations::getPercentile( unsigned percent ) const
		throw(invalid_argument) {
	if( percent == 0 || percent > 100 )
		throw invalid_argument( "percent should be in (0,100]" );
	// TODO: calculate stock price percentile
	return 0;
}
